#include "c2048.h"

void transpose(std::vector<std::vector<int> > &v) {
    int tmp;

    for(int i = 0; i < 4; i++) {
        for(int j = i + 1; j < 4; j++) {
            tmp = v[i][j];
            v[i][j] = v[j][i];
            v[j][i] = tmp;
        }
    }
}

C2048::C2048(QObject *parent): QObject(parent), m_rows(4), m_columns(4), m_score(0), m_bestScore(0) {
    init();
}

C2048::~C2048() {

}

int C2048::rows() const {
    return m_rows;
}
void C2048::setRows(const int rows) {
    m_rows = rows;
    //emit rowsChanged();
}

int C2048::columns() const {
    return m_columns;
}
void C2048::setColumns(const int columns) {
    m_columns = columns;
    emit columnsChanged();
}

int C2048::score() const {
    return m_score;
}

int C2048::bestScore() const {
    return m_bestScore;
}

int C2048::show(const int i, const int j) {
    return m_number[i][j];
}

QColor C2048::color(const int i, const int j) {
    int number = m_number[i][j];

    switch(number) {
        case 2:
            return QColor("#EEE4DA");;
            break;

        case 4:
            return QColor("#EDE0C8");
            break;

        case 8:
            return QColor("#F2B179");
            break;

        case 16:
            return QColor("#F59563");
            break;

        case 32:
            return QColor("#F67C5F");
            break;

        case 64:
            return QColor("#F65E3B");
            break;

        case 128:
            return QColor("#EDCF72");
            break;

        case 256:
            return QColor("#EDCC61");
            break;

        case 512:
            return QColor("#EDC850");
            break;

        case 1024:
            return QColor("#EDC53F");
            break;

        case 2048:
            return QColor("#EDC22E");
            break;

        default:
            return QColor("#CCC0B3");
            break; // black
    }
}

/*
    void C2048::back(){
	if(0 < m_step){
		m_number = m_state[m_step - 1 ];
		m_state.pop_back();
		m_step -= 1;
	}
    }
*/

void C2048::init() {
    m_number.resize(m_rows);

    for(int i = 0; i < m_rows; i++) {
        m_number[i].resize(m_columns);
    }

    for(int i = 0; i < m_rows; i++) {
        for(int j = 0; j < m_columns; j++) {
            m_number[i][j] = 0;
        }
    }

    createNewTile(true);

    m_score = 0;
    //m_step = 0;
    //m_state.push_back(m_number);
}

void C2048::createNewTile(bool isStartup) {
    QTime t = QTime::currentTime();
    qsrand(t.msec() + t.second() * 1000);
    int nTiles;

    if(isStartup) {
        nTiles = 2;
    } else {
        nTiles = 1;
    }

    // Popup a new number
    for(int i = 0; i < nTiles; i++) {
        int oneOrTwo = qrand() % 10 + 1 > 9 ? 4 : 2;

        while(true) {
            int randCellId1 = qrand() % m_rows;
            int randCellId2 = qrand() % m_columns;

            if(m_number[randCellId1][randCellId2] != 0) {
                continue;
            } else {
                m_number[randCellId1][randCellId2] = oneOrTwo;
                break;
            }
        }
    }
}

void C2048::moveKey(Move_Direction direc) {
    bool isMoved = false;
    int oldScore = m_score;

    switch(direc) {
        case Move_Up:
            transpose(m_number);

            for(int i = 0; i < m_columns; i++) {
                mergeVector(m_number[i]);
            }

            transpose(m_number);
            break;

        case Move_Down:
            transpose(m_number);

            for(int i = 0; i < m_rows; i++) {
                reverse(m_number[i].begin(), m_number[i].end());
                mergeVector(m_number[i]);
                reverse(m_number[i].begin(), m_number[i].end());
            }

            transpose(m_number);
            break;

        case Move_Left:
            for(int i = 0; i < m_columns; i++) {
                mergeVector(m_number[i]);
            }

            break;

        case Move_Right:
            for(int i = 0; i < m_rows; i++) {
                reverse(m_number[i].begin(), m_number[i].end());
                mergeVector(m_number[i]);
                reverse(m_number[i].begin(), m_number[i].end());
            }

            break;

        default:
            break;
    }

    /*
        if(isMoved) {
        updateAvailableCells();
        createNewTileItems(false);

        if(oldScore != m_score) {
            if(bestScore < m_score) {
                bestScore = m_score;
            }

            setScore(oldScore);
        }
        } else {
        if(isDead()) {
            deadMessage.open();
        }
        }
    */
}

void C2048::mergeVector(std::vector<int> &v0) {
    int vlen = v0.size();
    //vector<int> indices(4);
    // Pass 1: remove zero elements
    std::vector<int> v;

    for(int i = 0; i < vlen; i++) {
        //indices[i] = v.size();

        if(v0[i] > 0) {
            v.push_back(v0[i]);
        }
    }

    // Pass 2: merge same elements
    std::vector<int> v2;

    for(int i = 0; i < v.size(); i++) {
        if(i == v.size() - 1) {
            // The last element
            v2.push_back(v[i]);
        } else {
            if(v[i] == v[i + 1]) {
                // Merge i-1 and i
                v2.push_back(v[i] * 2);
                //score += Math.pow(2, v[i] + 1);
                i++;
            } else {
                v2.push_back(v[i]);
            }
        }
    }

    // Fill the gaps with zeros
    for(int i = v2.size(); i < vlen; i++) {
        v2.push_back(0);
    }

    v0 = v2;
}
